package com.zxjbyte.yiyi.module.system.endpoint.oss;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import com.zxjbyte.yiyi.framework.common.domain.api.R;
import com.zxjbyte.yiyi.framework.common.exception.ServiceException;
import com.zxjbyte.yiyi.framework.common.util.ValidationUtil;
import com.zxjbyte.yiyi.framework.oss.client.FileClient;
import com.zxjbyte.yiyi.framework.oss.storage.FileMetadata;
import com.zxjbyte.yiyi.framework.oss.storage.OssPutResult;
import com.zxjbyte.yiyi.module.system.util.OptionalUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @Author zhangxingjia
 * @Date 2023/6/29 11:20
 * @Version: 1.0
 */
@Slf4j
@Tag(name = "oss对象存储端点")
@RestController
@RequestMapping(value = "/system/oss")
public class OssEndPoint {

    @Resource
    private Optional<FileClient> client;

    @Operation(summary = "上传文件")
    @SneakyThrows
    @PostMapping("/putfile")
    public R<?> putFile(@RequestPart("file") MultipartFile file){
        return R.data(executePutFile(file.getInputStream(), file.getOriginalFilename()));
    }

    @Operation(summary = "批量上传文件")
    @PostMapping("/putfilebatch")
    public R<?> putFileBatch(@RequestPart("files") MultipartFile[] files){
        if(files == null){
            return R.fail("请选择上传文件");
        }
        List<OssPutResult> result = Arrays.stream(files).map(file -> {
            try {
                return executePutFile(file.getInputStream(), file.getOriginalFilename());
            } catch (Exception e) {
                throw new ServiceException("上传失败: " + e.getMessage());
            }
        }).collect(Collectors.toList());
        return R.data(result);
    }

    private OssPutResult executePutFile(InputStream input, String originalFilename) {
        ValidationUtil.assertNotBlank(originalFilename, () -> "请选择需要上传的文件");
        return getClient().putFile(input, originalFilename);
    }

    @Operation(summary = "查看文件", description = "仅限Local和Db存储器调用")
    @SneakyThrows
    @GetMapping("/get/**")
    public ModelAndView get(HttpServletRequest request, HttpServletResponse response){
        // 获取请求的路径
        String path = StrUtil.subAfter(request.getRequestURI(), "/get/", false);
        FileMetadata metadata;
        if (StrUtil.isBlank(path) || ((metadata = getClient().statFile(path)) == null || metadata.getFileContent() == null)) {
            log.warn("Download files [path: {}, error info: file does not exist!]", path);
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            // 返回 404.html 视图
            ModelAndView modelAndView = new ModelAndView("error/404");
            modelAndView.addObject("path", path);
            return modelAndView;
        }else{
            // 生成 ETag 和 Last-Modified 时间戳
            String eTag = metadata.getHash();
            String lastModified = DateUtil.format(metadata.getPutTime(), DatePattern.HTTP_DATETIME_FORMAT);
            // 检查请求头中的 If-None-Match 和 If-Modified-Since
            String ifNoneMatch = request.getHeader(HttpHeaders.IF_NONE_MATCH);
            String ifModifiedSince = request.getHeader(HttpHeaders.IF_MODIFIED_SINCE);
            if(StrUtil.equals(eTag, ifNoneMatch) && StrUtil.equals(lastModified, ifModifiedSince)){
                response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                return null;
            }
            byte[] content = metadata.getFileContent();
            // 这里不具体设置Content-Disposition头字段，默认浏览器会根据响应的MIME类型进行处理，
            // 对于图片、音频、视频等媒体类型，浏览器会自动以嵌入或者下载的方式处理，具体行为取决于浏览器的设置和用户的偏好。
            // 对于其他文件类型（如 PDF、Word 文档、Excel 表格等），浏览器通常会以下载的方式处理，提示用户保存文件或者直接打开
            response.setContentType(FileUtil.getMimeType(path));
            response.setContentLength(content.length);
            response.setHeader(HttpHeaders.ETAG, eTag);
            response.setHeader(HttpHeaders.LAST_MODIFIED, lastModified);
            // 输出附件
            IoUtil.write(response.getOutputStream(), false, content);
            return null;
        }
    }

    @Operation(summary = "删除文件")
    @PostMapping("/delete")
    public R<?> deleteFile(@RequestParam String path){
        getClient().deleteFile(path);
        return R.ok();
    }

    private FileClient getClient(){
        return OptionalUtil.getOssInjectOrThrow(client);
    }
}
